# -*- -*- -*- -*- -*- -*- -*-
# @Author  : mengzhe
# @Time    : 2025/5/26 11:19
# @File    : client.py
# @function:
# @input   :
# @output  :
# @Software: PyCharm 
# -*- -*- -*- -*- -*- -*- -*-
import socket
import time
from datetime import datetime, timedelta
import threading
from plc import PLC
from weather_station import WEATHER
from radar import ASSRRadarServer
import utils
import struct

HOST = 'localhost'  # 主服务器地址
PORT = 12345  # 主服务器端口
RADAR_HOST = '192.168.1.135'  # 雷达IP
RADAR_PORT = 3000  # 雷达端口


class ClientConnection:
    def __init__(self):
        self.lock = threading.Lock()
        self.active = False
        self.sock = None  # 主服务器socket
        self.radar_sock = None  # 雷达socket
        self.reconnect_flag = False
        self.last_plc_minute = -1
        self.last_weather_minute = -1
        self.last_time_minute = -1
        self.last_radar_minute = -1
        self.message_counter = 0
        self.counter_lock = threading.Lock()
        self.plc_id = b'\x1A\x00\x00\x00'
        self.plc_id_lock  = threading.Lock()

        # 初始化传感器
        self.plc = PLC(sensor_id="PLC001", name="Industrial_PLC")
        self.weather = WEATHER(sensor_id="WS001", name="Weather_Station")
        self.radar = ASSRRadarServer(host=RADAR_HOST, port=RADAR_PORT)
        self.last_sensor_minute = -1

    def get_next_counter(self):
        with self.counter_lock:
            self.message_counter += 1
            return self.message_counter
    def get_plc_id(self):
        with self.plc_id_lock:
            return self.plc_id

    # def connect_to_radar(self):
    #     """连接雷达设备，最多尝试10次"""
    #     max_retries = 10
    #     retry_count = 0
    #
    #     while retry_count < max_retries and self.active:
    #         try:
    #             self.radar_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #             self.radar_sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
    #             self.radar_sock.bind((RADAR_HOST, RADAR_PORT))
    #             self.radar_sock.listen(1)
    #             print("[Radar] Waiting for connection...")
    #             # 等待连接
    #             self.radar_sock, self.radar_client_address = self.radar_sock.accept()
    #             # self.radar_sock.connect((RADAR_HOST, RADAR_PORT))
    #             print("[Radar] Connected to radar successfully")
    #             return True
    #         except Exception as e:
    #             retry_count += 1
    #             remaining = max_retries - retry_count
    #             print(
    #                 f"[Radar] Connection failed ({retry_count}/{max_retries}): {str(e)}, retrying in 5s... ({remaining} attempts left)")
    #             time.sleep(5)
    #
    #     print("[Radar] Max retries reached, connection abandoned")
    #     return False

    def connect_to_radar(self):
        """连接雷达设备，最多尝试10次"""
        max_retries = 10
        retry_count = 0

        while retry_count < max_retries and self.active:
            try:
                self.radar.start_server()
                self.radar.initialize_radar()

                # # 查询雷达状态
                # self.radar.query_status()
                # # 查询场景参数
                # self.radar.query_scene_params()

                return True
            except Exception as e:
                retry_count += 1
                remaining = max_retries - retry_count
                print(f"[Radar] Connection failed ({retry_count}/{max_retries}): {str(e)}, retrying in 5s... ({remaining} attempts left)")
                time.sleep(5)
        print("[Radar] Max retries reached, connection abandoned")
        return False

    def process_command(self, cmd):
        """处理来自主服务器的命令"""

        # 设备ID（1字节）
        device_id = cmd[6]

        # 命令码（1字节）
        command_code = cmd[7]

        # 扩展码
        extension_code = cmd[8]

        cmd_data = cmd[38:]
        # 处理PLC相关命令


        if device_id==utils.PLC_ID:
            if command_code == 0x00:
                # 主动上报
                print("主动上报")
                return self.plc.active_report()
            elif command_code == 0x01:
                # 服务器查询
                print("服务器查询")
                return self.plc.query_report()
            else:
                print("指令执行异常")
                return f"ERROR:Unknown command for Control PC: {command_code}"

        elif device_id==utils.RADAR_ID:
            if command_code == 0x00:
                # 场景参数、输出参数设置
                if extension_code == 0x00:
                    # 场景参数
                    print("设置场景参数")
                    return self.radar.set_scene_params(cmd_data)
                elif extension_code == 0x01:
                    # 输出参数
                    print("设置输出参数")
                    return self.radar.set_output_params(cmd_data)
            elif command_code == 0x01:
                # 场景参数、输出参数查询
                if extension_code == 0x00:
                    print("查询场景参数")
                    return self.radar.query_scene_params()
                elif extension_code == 0x01:
                    print("查询输出参数")
                    return self.radar.query_output_params()
            elif command_code == 0x02:
                # 查询状态命令
                if extension_code == 0x00:
                    # print("查询状态")
                    print(f"[Radar][{datetime.now().strftime('%H:%M:%S.%f')[:-3]}] Query status")
                    return self.radar.query_status()
            elif command_code == 0x03:
                # 工作指令及其他命令格式
                if extension_code == 0x00:
                    print("开始工作")
                    return self.radar.start_work()
            elif  command_code == 0x04:
                # 停止工作
                if extension_code == 0x00:
                    print("停止工作")
                    return self.radar.stop_work()
            elif command_code == 0x05:
                # 重启
                if extension_code == 0x00:
                    print("重启")
                    return self.radar.reboot()
            elif command_code == 0x06:
                # 升级指令
                if extension_code == 0x00:
                    print("升级指令,暂未开放")
                    return self.radar.query_status()
            elif command_code == 0x07:
                # 雷达休眠
                if extension_code == 0x00:
                    print("雷达休眠")
                    return self.radar.sleep()
                elif extension_code == 0x01:
                    print("雷达唤醒")
                    return self.radar.wakeup()

            elif command_code == 0x08:
                # 日志列表获取
                if extension_code == 0x00:
                    print("日志列表获取")
                    return self.radar.get_log_list()
                elif extension_code == 0x01:
                    print("日志获取")
                    return self.radar.get_log(cmd_data)


        elif device_id==utils.CAMERA_ID:
            print('nihao')

        elif device_id==utils.GNSS_ID:
            print('nihao')

        elif  device_id==utils.WEATHER_ID:
            if command_code == 0x00:
                # 主动上报
                print("读取数据")
                return self.weather.active_report()
            elif command_code == 0x01:
                # 服务器查询
                print("服务器查询")
                return self.weather.query_report()


            print('nihao')

        elif device_id==utils.ROUTE_ID:
            print('nihao')



    def radar_data_receiver(self):
        """接收雷达主动上报数据的线程"""
        cmd = b'U\xaa\x1a\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        while self.active:
            try:
                if not self.radar.client_socket:
                    time.sleep(1)
                    continue

                # 接收雷达数据
                # data = self.radar.client_socket.recv(2048)
                header = self.radar.client_socket.recv(28)
                # print(f"接收指令头: {header.hex()}")
                if not header:
                    print("雷达连接已关闭")
                    break

                if len(header) < 28:
                    print("接收到的数据头不完整")
                    continue

                # 解析数据头
                command_header = struct.unpack('<H', header[:2])[0]
                device_id = struct.unpack('<I', header[2:6])[0]
                command_code = header[6]
                ext_code = header[7]
                status_field = header[8:24]
                data_length = struct.unpack('<I', header[24:28])[0]

                # print(f"数据头: 命令头 {hex(command_header)}, 设备ID {device_id}, 命令码 {hex(command_code)}, 扩展码 {hex(ext_code)}")

                # 验证命令头
                if header[0:2] != b'\x3c\x3c':  # 雷达数据头
                    print(f"无效的数据头: {header.hex()}")
                    continue

                # 读取数据部分
                data = b''
                if data_length > 0:
                    remaining = data_length
                    while remaining > 0:
                        chunk = self.radar.client_socket.recv(remaining)
                        if not chunk:
                            print("接收数据中断")
                            break
                        data += chunk
                        remaining -= len(chunk)

                if not header:
                    print("[Radar] Radar disconnected")
                    self.radar.client_socket.close()
                    self.radar.client_socket = None
                    continue



                current_time = datetime.now().strftime("%H:%M:%S.%f")[:-3]
                print(f"[Radar][{current_time}] 雷达监听程序")
                print(f"[Radar][{current_time}] Received data: {header}{data[0:100]}")
                print(f"[Radar][{current_time}] Received command:{header.hex()} {data[0:100].hex()}")

                # 针对radar上报信息进行更新
                self.radar.handle_cmd_radar_data(header,data)

                # 更新编号
                counter = self.get_next_counter()
                counter_bytes = counter.to_bytes(length=4, byteorder='little', signed=False)
                cmd_count = utils.replace_count_command_ext(cmd, counter_bytes,header)

                command_results = utils.command_processor(cmd_count, data)

                print(f"[Radar][{current_time}] Send data: {command_results[1:100]}")
                print(f"[Radar][{current_time}] Send command:{command_results.hex()} ")

                message = f"[RADAR_REPORT] {data.hex()} [CNT] {counter}"

                with self.lock:
                    if self.active and self.sock:
                        try:
                            self.sock.sendall(command_results)
                            print(f"[Client][{current_time}] Forwarded radar data (Count: {counter})")
                        except (OSError, ConnectionError) as e:
                            print(f"[Client] Radar data forward error: {str(e)}")
                            self.reconnect_flag = True
                            break

            except socket.timeout:
                continue
            except Exception as e:
                print(f"[Radar] Receiver error: {str(e)}")
                if self.radar.client_socket:
                    self.radar.client_socket.close()
                    self.radar.client_socket = None
                time.sleep(1)

    def time_sender(self):
        """时间上报线程"""
        while self.active:
            try:
                now = datetime.now()
                current_minute = now.minute

                if current_minute != self.last_minute:
                    self.last_minute = current_minute

                    beijing_time = now.strftime("%Y-%m-%d %H:%M:%S")
                    counter = self.get_next_counter()
                    message = f"[TIME] {beijing_time} [CNT] {counter}"

                    with self.lock:
                        if self.active and self.sock:
                            try:
                                self.sock.sendall(message.encode())
                                current_time = now.strftime("%H:%M:%S.%f")[:-3]
                                print(f"[Client][{current_time}] Sent time report (Count: {counter})")
                            except (OSError, ConnectionError) as e:
                                print(f"[Client] Send error: {str(e)}")
                                self.reconnect_flag = True
                                break

                    next_minute = (now + timedelta(minutes=1)).replace(second=0, microsecond=0)
                    delay = (next_minute - now).total_seconds()
                    time.sleep(max(0, min(delay, 60)))
                else:
                    time.sleep(1)

            except Exception as e:
                print(f"[Client] Time sender error: {str(e)}")
                self.reconnect_flag = True
                break

    def plc_data_sender(self):
        """传感器数据上报线程"""
        cmd = b'U\xaa\x1a\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        while self.active:
            try:
                now = datetime.now()
                current_minute = now.minute

                if current_minute != self.last_plc_minute:
                    self.last_plc_minute = current_minute

                    # PLC数据上报
                    # plc_data = self.plc.active_escalation()
                    plc_data = self.plc.query_report()
                    counter = self.get_next_counter()
                    counter_bytes= counter.to_bytes(length=4, byteorder='little', signed=False)
                    cmd_count= utils.replace_count_bytes(cmd,counter_bytes)

                    command_results = utils.command_processor(cmd_count, plc_data)
                    message = f"[PLC_REPORT] {plc_data} [CNT] {counter}"

                    with self.lock:
                        if self.active and self.sock:
                            try:
                                self.sock.sendall(command_results)
                                current_time = now.strftime("%H:%M:%S.%f")[:-3]
                                print(f"[Client][{current_time}] Sent PLC report (Count: {counter})")
                            except (OSError, ConnectionError) as e:
                                print(f"[Client] PLC send error: {str(e)}")
                                self.reconnect_flag = True
                                break

                    # 同步等待
                    if current_minute == self.last_minute:
                        next_minute = (now + timedelta(minutes=1)).replace(second=0, microsecond=0)
                        delay = (next_minute - now).total_seconds()
                        time.sleep(max(0, min(delay, 60)))
                else:
                    time.sleep(1)

            except Exception as e:
                print(f"[Client] Sensor sender error: {str(e)}")
                self.reconnect_flag = True
                break
    def weather_data_sender(self):
        """传感器数据上报线程"""
        cmd=b'U\xaa\x1a\x00\x00\x00\x04\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        while self.active:
            try:
                now = datetime.now()
                current_minute = now.minute

                if current_minute != self.last_weather_minute:
                    self.last_weather_minute = current_minute

                    # 气象站数据上报
                    # weather_data = self.weather.active_escalation()
                    weather_data = self.weather.active_report()
                    counter = self.get_next_counter()
                    message = f"[WS_REPORT] {weather_data} [CNT] {counter}"

                    counter_bytes= counter.to_bytes(length=4, byteorder='little', signed=False)
                    cmd_count= utils.replace_count_bytes(cmd,counter_bytes)

                    command_results = utils.command_processor(cmd_count, weather_data)

                    with self.lock:
                        if self.active and self.sock:
                            try:
                                self.sock.sendall(command_results)
                                current_time = now.strftime("%H:%M:%S.%f")[:-3]
                                print(f"[Client][{current_time}] Sent Weather report (Count: {counter})")
                            except (OSError, ConnectionError) as e:
                                print(f"[Client] Weather send error: {str(e)}")
                                self.reconnect_flag = True
                                break

                    # 同步等待
                    if current_minute == self.last_minute:
                        next_minute = (now + timedelta(minutes=1)).replace(second=0, microsecond=0)
                        delay = (next_minute - now).total_seconds()
                        time.sleep(max(0, min(delay, 60)))
                else:
                    time.sleep(1)

            except Exception as e:
                print(f"[Client] Sensor sender error: {str(e)}")
                self.reconnect_flag = True
                break

    def run(self):
        """主运行循环"""
        while True:
            self.reconnect_flag = False
            try:
                # 连接主服务器
                with socket.create_connection((HOST, PORT), timeout=30) as sock:
                    self.sock = sock
                    self.active = True
                    self.last_minute = datetime.now().minute
                    self.last_time_minute  = self.last_minute
                    self.last_plc_minute = self.last_minute
                    self.last_weather_minute = self.last_minute
                    self.last_radar_minute = self.last_minute

                    print("[Client] Connected to main server!")

                    # 连接雷达
                    if self.connect_to_radar():
                        # 启动雷达得TCP线程
                        # print("[Client] Connected to radar")
                        current_time = datetime.now().strftime("%H:%M:%S.%f")[:-3]
                        print(f"[Client][{current_time}] Connected to radar")
                        threading.Thread(target=self.radar_data_receiver, daemon=True).start()

                    # # # 启动其他线程
                    # threading.Thread(target=self.time_sender, daemon=True).start()
                    # threading.Thread(target=self.plc_data_sender, daemon=True).start()
                    # threading.Thread(target=self.weather_data_sender, daemon=True).start()



                    # 主命令循环
                    while self.active:
                        try:
                            sock.settimeout(5)
                            data = sock.recv(1024)
                            if not data:
                                print("[Client] Server closed connection")
                                break

                            if data[0]!=0x55 or data[1]!=0xAA:
                                print("[Client] Received invalid command")
                                sock.send(utils.receive_cmd_error(data,  b'\x06'))
                                continue

                            if data[2:6] !=self.get_plc_id():
                                print("[Client] Received invalid PLC ID")
                                sock.send(utils.receive_cmd_error(data, b'\x05'))
                                continue

                            cmd = data
                            current_time = datetime.now().strftime("%H:%M:%S.%f")[:-3]
                            print(f"[Client][{current_time}] Received command: {cmd}")



                            feedback = self.process_command(cmd)
                            counter = self.get_next_counter()
                            full_feedback = f"{feedback} [CNT] {counter}"

                            command_results = utils.command_processor(cmd, feedback)

                            with self.lock:
                                if self.active:
                                    if isinstance(command_results, bytes):
                                        sock.send(command_results)
                                    elif isinstance(command_results, list):
                                        for command_result in command_results:
                                            sock.send(command_result)
                                    sock.sendall(full_feedback.encode())
                                    current_time = datetime.now().strftime("%H:%M:%S.%f")[:-3]
                                    print(f"[Client][{current_time}] Sent feedback (Count: {counter})")

                        except socket.timeout:
                            continue
                        except (ConnectionResetError, BrokenPipeError) as e:
                            print(f"[Client] Connection error: {str(e)}")
                            self.reconnect_flag = True
                            break
                        except Exception as e:
                            print(f"[Client] Unexpected error: {str(e)}")
                            self.reconnect_flag = True
                            break

            except socket.timeout:
                print("[Client] Connection timeout, retrying in 5 seconds ...")
                time.sleep(5)
            except ConnectionRefusedError:
                print("[Client] Server unavailable, retrying in 5 seconds ...")
                self.radar.stop_server()
                time.sleep(5)
            except Exception as e:
                print(f"[Client] Connection failed: {str(e)}")

            # 清理资源
            self.active = False
            if self.sock:
                try:
                    self.sock.close()
                except:
                    pass
                self.sock = None

            if self.radar_sock:
                try:
                    self.radar_sock.close()
                except:
                    pass
                self.radar_sock = None

            if self.reconnect_flag:
                print("[Client] Reconnecting...")
                self.radar.stop_server()
                time.sleep(1)



if __name__ == "__main__":
    client = ClientConnection()
    client.run()